#include "EquityCalculator.h"
#include "Util.h"
#include "../libdivide/libdivide.h"
#include <random>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <set>
#include "WeightedCardRange.h"
#include <iterator>
#include <ostream>
#include <iomanip>
#include <mutex>
#include <unordered_map>
#include <vector>
#include <sstream>

static std::mutex cout_mutex;


namespace omp {

    /**
 * @brief 启动加权蒙特卡洛模拟或全枚举计算（支持double类型权重）
 * 
 * 初始化加权 equity 计算环境，配置线程池并开始异步计算。支持自定义终止条件（标准差目标）
 * 和进度回调，适用于非均匀概率分布的手牌范围评估场景。
 * 
 * @param[in] handRanges 加权手牌范围向量，每个元素包含特定玩家的所有可能手牌组合及其权重
 * @param[in] boardCards 底牌掩码（可选，默认0），使用位运算表示已发公共牌
 * @param[in] deadCards 死牌掩码（可选，默认0），使用位运算表示不可用牌
 * @param[in] stdevTarget 标准差目标（可选，默认0.00005），达到该值时停止模拟
 * @param[in] callback 进度回调函数（可选），每次结果更新时触发
 * @param[in] updateInterval 回调更新间隔（可选，默认0.2秒）
 * @param[in] threadCount 线程数量（可选，默认0表示自动分配）
 * @return bool 启动成功返回true，失败返回false
 * @note 1. 手牌范围为空时会立即返回false并输出错误信息
 * @note 2. 当enumerateAll标志为true时自动切换为全枚举模式
 * @note 3. 权重值通过WeightedCardRange结构体传入，内部自动处理归一化
 * @note 4. 线程安全设计，支持多线程并行计算
 */
bool EquityCalculator::start_v2(const std::vector<WeightedCardRange>& handRanges, uint64_t boardCards, uint64_t deadCards,
                                double stdevTarget, std::function<void(const Results&)> callback,
                                double updateInterval, unsigned threadCount)
{
    // 检查手牌范围是否为空
    for (const auto& range : handRanges) {
        if (range.getWeightedCombos().size() == 0) {
            std::cerr << "Error: Hand range is empty!" << std::endl;
            return false;
        }
    }
    // std::cout << "Starting simulation with weighted hand ranges:" << std::endl;
    // 输出所有玩家的手牌范围及其权重
    for (unsigned playerIdx = 0; playerIdx < handRanges.size(); ++playerIdx) {
        const auto& combos = handRanges[playerIdx].getWeightedCombos();
        // 调试输出
        // std::cout << "Player " << playerIdx << " combos size:" <<combos.size() <<std::endl;
        // std::cout << "Player " << playerIdx << " hand ranges:" << std::endl;
        // for (const auto& combo : combos) {
        //     std::array<uint8_t, 2> hand = combo.first;
        //     std::string handStr = omp::CardRange::handToString(hand);
        //     std::cout << "  " << handStr << ":" << std::fixed << std::setprecision(10) << combo.second << std::endl;
        // }
    }
    // 参数检查
    if (handRanges.size() == 0 || handRanges.size() > MAX_PLAYERS)
        return false;
    if (bitCount(boardCards) > BOARD_CARDS)
        return false;
    if (2 * handRanges.size() + bitCount(deadCards) + BOARD_CARDS > CARD_COUNT)
        return false;

    // 初始化成员变量
    mDeadCards = deadCards;
    mBoardCards = boardCards;
    mStdevTarget = (stdevTarget <= 0) ? 0.00022: stdevTarget; // 设置默认标准差目标
    mCallback = callback;
    mUpdateInterval = updateInterval;
    mStopped = false;
    mLastUpdate = std::chrono::high_resolution_clock::now();
    if (threadCount == 0)
        threadCount = std::thread::hardware_concurrency();
    mUnfinishedThreads = threadCount;

    // 保存原始加权手牌范围
    mOriginalHandRanges.clear();
    for (const auto& range : handRanges) {
        mOriginalHandRanges.emplace_back(range);
    }
    mHandRanges.clear();
    for (const auto& wcr : handRanges) {
        std::vector<std::array<uint8_t,2>> combos;
        for (const auto& p : wcr.getWeightedCombos())
            combos.push_back(p.first);
        mHandRanges.push_back(combos);
    }
    // 过滤与公共牌和死牌冲突的手牌组合
    uint64_t reservedCards = mBoardCards | mDeadCards;
    std::vector<CardRange> ranges;
    for (const auto& wcr : handRanges) {
        std::vector<std::array<uint8_t,2>> combos;
        for (const auto& p : wcr.getWeightedCombos()) {
            combos.push_back(p.first);
        }
        ranges.emplace_back(combos);
    }
    mHandRanges = removeInvalidCombos(ranges, reservedCards);
    mCombinedRangeCount = (unsigned)mHandRanges.size();

    bool isEnumerateAll = mResults.enumerateAll;
    // 结果初始化
    mResults = Results();
    mResults.players = (unsigned)handRanges.size();
    // mResults.enumerateAll = false;
    mResults.enumerateAll = isEnumerateAll;
    mResults.totalAllSum = 0.0;
    mResults.totalWinSqrSum = 0.0;
    mUpdateResults = mResults;
    mResults.typeCounts.resize(mResults.players);
    mResults.typeWins.resize(mResults.players); 
    mEnumPosition = 0; // 重置枚举循环位置为0

    // 在Results初始化后重新设置isWeightedMonteCarlo
    mResults.isWeightedMonteCarlo = true;

    // 创建过滤后的加权手牌范围
    std::vector<WeightedCardRange> filteredHandRanges;
    for (unsigned i = 0; i < handRanges.size(); ++i) {
        const auto& wcr = handRanges[i];
        std::stringstream ss;
        bool first = true;
        for (const auto& p : wcr.getWeightedCombos()) {
            uint64_t handMask = (1ull << p.first[0]) | (1ull << p.first[1]);
            if (!(reservedCards & handMask)) {
                if (!first) ss << ",";
                ss << CardRange::handToString(p.first) << ":" << p.second;
                first = false;
            }
        }
        // 使用构建的字符串表达式创建WeightedCardRange对象
        // 第二个参数设为false，因为不用重新归一化权重
        filteredHandRanges.emplace_back(ss.str(), false, false);
    }
    // 初始化组合范围（使用WeightedCardRange构造函数，保留权重信息）
    mCombinedRangeCount = 0;
    if (filteredHandRanges.size() > 0) {
        mCombinedRanges[mCombinedRangeCount++] = CombinedRange(0, filteredHandRanges[0]);
        for (unsigned i = 1; i < filteredHandRanges.size(); ++i) {
            mCombinedRanges[mCombinedRangeCount++] = CombinedRange(i, filteredHandRanges[i]);
        }
    }
    // std::cout << "\nmCombinedRangeCount: " << mCombinedRangeCount << std::endl;
    // for (unsigned i = 0; i < mCombinedRangeCount; ++i) {
    //     std::cout << "CombinedRanges[" << i << "].combos().size(): " << mCombinedRanges[i].combos().size() << std::endl;
    // }
    //         std::cout << "Before start enumerateAll: " << mResults.enumerateAll << std::endl;
    // 线程启动
    mThreads.clear();
    for (unsigned i = 0; i < threadCount; ++i) {
        mThreads.emplace_back([this]{
            if (mResults.enumerateAll) {
                enumerateWeighted();  // 加权全枚举
            } else {
                simulateWeightedRegularMonteCarlo(mOriginalHandRanges, mBoardCards, mDeadCards, mStdevTarget);
            }
        });
    }

    return true;
}

// 加权全枚举流程
void EquityCalculator::enumerateWeighted()
{
    // std::cout <<"\n[DEBUG] Entry enumerateWeighted" << std::endl;
    uint64_t enumEnd = 0;
    uint64_t preflopCombos = getPreflopCombinationCount();
    std::lock_guard<std::mutex> lock(cout_mutex);
    // std::cout << "[Thread " << std::this_thread::get_id() << "] [DEBUG] Initial mEnumPosition: " << mEnumPosition << std::endl;
    
    uint64_t batchSize = std::max<uint64_t>(2000000 / getPostflopCombinationCount(), 1);
    // std::cout << "mEnumPosition: " << mEnumPosition<< ", enumEnd : " << enumEnd << std::endl;

    unsigned nplayers = (unsigned)mOriginalHandRanges.size();
    BatchResults stats(nplayers);
    UniqueRng64 urng(preflopCombos);
    Hand fixedBoard = getBoardFromBitmask(mBoardCards);
    libdivide::libdivide_u64_t fastDividers[MAX_PLAYERS];

    unsigned combinedRangeCount = mCombinedRangeCount;
    for (unsigned i = 0; i < combinedRangeCount; ++i)
        fastDividers[i] = libdivide::libdivide_u64_gen(mCombinedRanges[i].combos().size());

    uint64_t postflopCombos = getPostflopCombinationCount();
    // std::cout << "[DEBUG] getPostflopCombinationCount() returned: " << postflopCombos << std::endl;
    bool useLookup = getPostflopCombinationCount() > 500;
    bool randomizeOrder = getPostflopCombinationCount() > 10000 && preflopCombos <= 2 * MAX_LOOKUP_SIZE;
    for (;mEnumPosition < preflopCombos; ++mEnumPosition) {
        if (mEnumPosition >= enumEnd) {
            std::tie(mEnumPosition, enumEnd) = reserveBatch(batchSize);
            // 检查是否成功获取批次
            if (mEnumPosition >= enumEnd) {
                std::cerr << "Failed to reserve initial batch!" << std::endl;
                return;
            }
        }

        uint64_t randomizedEnumPos = randomizeOrder ? urng(mEnumPosition) : mEnumPosition;
        // 全新的严格冲突检测系统
        bool ok = true;
        uint64_t forbiddenCards = mBoardCards | mDeadCards;

        // 在 usedCardsMask 初始化之后添加
        std::vector<uint64_t> playerCardMasks(nplayers, 0);

        HandWithPlayerIdx playerHands[MAX_PLAYERS] = {}; 
        double comboWeight = 1.0;
        
        // 解析手牌组合并计算权重
        for (unsigned i = 0; i < combinedRangeCount; ++i) {
            uint64_t quotient = libdivide_u64_do(randomizedEnumPos, &fastDividers[i]);
            uint64_t remainder = randomizedEnumPos - quotient * mCombinedRanges[i].combos().size();
            randomizedEnumPos = quotient;

            const CombinedRange::Combo& combo = mCombinedRanges[i].combos()[(size_t)remainder];

            // 1. 严格的三重冲突检测
            uint64_t comboHandMask = 0;
            
            // 检查每个玩家的手牌
            for (unsigned j = 0; j < mCombinedRanges[i].playerCount(); ++j) {
                const auto& holeCards = combo.holeCards[j];
                uint64_t handMask = (1ull << holeCards[0]) | (1ull << holeCards[1]);
                
                // 检查与公共牌和死牌的冲突
                if (handMask & forbiddenCards) {
                    ok = false;
                    break;
                }
                
                // 检查与其他玩家手牌的冲突
                if (handMask & comboHandMask) {
                    ok = false;
                    break;
                }
                
                comboHandMask |= handMask;
            }
            if (!ok) break;

            // 2. 检查玩家分配是否有效
            for (unsigned j = 0; j < mCombinedRanges[i].playerCount(); ++j) {
                unsigned playerIdx = mCombinedRanges[i].players()[j];
                if (playerIdx >= MAX_PLAYERS || playerIdx >= mOriginalHandRanges.size()) {
                    ok = false;
                    break;
                }

                // 检查当前玩家是否已分配手牌
                if (playerCardMasks[playerIdx] != 0) {
                    ok = false;
                    break;
                }
            }

            if (!ok) break;

            // 3. 记录有效的手牌
            for (unsigned j = 0; j < mCombinedRanges[i].playerCount(); ++j) {
                unsigned playerIdx = mCombinedRanges[i].players()[j];
                const auto& holeCards = combo.holeCards[j];
                playerHands[playerIdx].playerIdx = playerIdx;
                playerHands[playerIdx].cards = holeCards;
                playerCardMasks[playerIdx] = (1ull << holeCards[0]) | (1ull << holeCards[1]);
            }

            // 4. 计算组合权重
            comboWeight = combo.weight;
        }

        if (!ok) {
            ++stats.skippedPreflopCombos;
            continue;
        }

        // 5. 统一处理所有模式
        uint64_t completeUsedCardsMask = mBoardCards | mDeadCards;
        for (unsigned j = 0; j < nplayers; ++j) {
            completeUsedCardsMask |= playerCardMasks[j];
        }

        if (useLookup) {
            for (unsigned i = 0; i < nplayers; ++i) {
                stats.playerIds[i] = playerHands[i].playerIdx;
            }

            // 转换花色以利用同构优化
            uint64_t transformedBoardCards = mBoardCards;
            uint64_t transformedDeadCards = mDeadCards;
            transformSuits(playerHands, nplayers, &transformedBoardCards, &transformedDeadCards);
            
            uint64_t transformedUsedCardsMask = transformedBoardCards | transformedDeadCards;
            for (unsigned j = 0; j < nplayers; ++j) {
                transformedUsedCardsMask |= playerCardMasks[j];
            }

            uint64_t preflopId = calculateUniquePreflopId(playerHands, nplayers);
            BatchResults cachedStats(nplayers);
            if (lookupResults(preflopId, cachedStats)) {
                // 对缓存结果应用当前组合权重
                for (unsigned i = 0; i < (1u << nplayers); ++i) {
                    stats.winsByPlayerMask_v2[i] += cachedStats.winsByPlayerMask_v2[i] * comboWeight;
                }
                // 复用牌型统计缓存
                for (unsigned i = 0; i < nplayers; ++i) {
                    for (const auto& entry : cachedStats.typeCounts[i]) {
                        const std::string& type = entry.first;
                        double count = entry.second;
                        stats.typeCounts[stats.playerIds[i]][type] += count * comboWeight;
                    }
                    for (const auto& entry : cachedStats.typeWins[i]) {
                        const std::string& type = entry.first;
                        double win = entry.second;
                        stats.typeWins[stats.playerIds[i]][type] += win * comboWeight;
                    }
                }
            } else {
                ++stats.uniquePreflopCombos;
                Hand board = getBoardFromBitmask(transformedBoardCards);
                enumerateBoardWeighted(playerHands, nplayers, board, transformedUsedCardsMask, comboWeight, &stats);
                storeResults(preflopId, stats);
            }
        } else {
            ++stats.uniquePreflopCombos;
            Hand board = getBoardFromBitmask(mBoardCards);
            enumerateBoardWeighted(playerHands, nplayers, board, completeUsedCardsMask, comboWeight, &stats);
        }

        // 定期更新结果
        if ((stats.evalCount >= 10000 || stats.skippedPreflopCombos >= 10000 || useLookup) && !mStopped) {
            updateResults(stats, false);
            stats = BatchResults(nplayers);
        }
    }
    updateResults(stats, true);
}
// 加权枚举的牌面遍历实现
void EquityCalculator::enumerateBoardWeighted(const HandWithPlayerIdx* playerHands, unsigned nplayers,
                                 const Hand& board, uint64_t usedCardsMask, double comboWeight, omp::EquityCalculator::BatchResults* stats)
{
    Hand hands[MAX_PLAYERS];
    for (unsigned i = 0; i < nplayers; ++i)
        hands[i] = Hand(playerHands[i].cards);

    unsigned remainingCards = BOARD_CARDS - board.count();
    if (remainingCards == 0) {
        // 使用CombinedRange中已经计算好的权重，不再重复计算
        double finalWeight = 1.0;
        unsigned winnersMask = 0;
        evaluateHands_v2(hands, nplayers, board, stats, finalWeight, &winnersMask);
        
        // 增加具体手牌胜率统计（remainingCards == 0的情况）
        unsigned winnerCount = bitCount(winnersMask);
        double winShare = winnerCount > 0 ? 1.0 / winnerCount : 0.0;
        for (unsigned j = 0; j < nplayers; ++j) {
            const std::string& key = CardRange::handToString(playerHands[j].cards);
            stats->typeCounts[j][key] += finalWeight;
            if (winnersMask & (1 << j)) {
                stats->typeWins[j][key] += winShare * finalWeight;
            }
        }
        return;
    }

    // 初始化牌堆（复用原项目排序逻辑）
    unsigned deck[CARD_COUNT];
    unsigned ndeck = 0;
    for (unsigned c = CARD_COUNT; c-- > 0;) {
        if(!(usedCardsMask & (1ull << c)))
            deck[ndeck++] = c;
    }

    // 计算花色计数（用于同花优化）
    unsigned suitCounts[SUIT_COUNT] = {};
    for (unsigned i = 0; i < nplayers; ++i) {
        if ((playerHands[i].cards[0] & 3) == (playerHands[i].cards[1] & 3)) {
            suitCounts[playerHands[i].cards[0] & 3] = std::max(2u, suitCounts[playerHands[i].cards[0] & 3]);
        } else {
            suitCounts[playerHands[i].cards[0] & 3] = std::max(1u, suitCounts[playerHands[i].cards[0] & 3]);
            suitCounts[playerHands[i].cards[1] & 3] = std::max(1u, suitCounts[playerHands[i].cards[1] & 3]);
        }
    }
    for (unsigned i = 0; i < SUIT_COUNT; ++i)
        suitCounts[i] += board.suitCount(i);

    // 启动递归枚举（使用CombinedRange的权重）
    enumerateBoardWeightedRec(hands, nplayers, stats, board, deck, ndeck, suitCounts, 
                           remainingCards, 0, comboWeight, playerHands);
}

void EquityCalculator::enumerateBoardWeightedRec(const Hand* playerHands, unsigned nplayers, omp::EquityCalculator::BatchResults* stats,
                                const Hand& board, unsigned* deck, unsigned ndeck, unsigned* suitCounts,
                                unsigned cardsLeft, unsigned start, double weight,
                                const HandWithPlayerIdx* originalPlayerHands)
{
    if (cardsLeft == 1)
    {
        if (suitCounts[0] < 4 && suitCounts[1] < 4 && suitCounts[2] < 4 && suitCounts[3] < 4) {
            for (unsigned i = start; i < ndeck; ) {
                unsigned multiplier = 1;
                Hand newBoard = board + deck[i];
                unsigned rank = deck[i] >> 2;
                for (++i; i < ndeck && deck[i] >> 2 == rank; ++i)
                    ++multiplier;

                // 使用CombinedRange中已经计算好的权重
        double finalWeight = weight * multiplier;

                unsigned winnersMask = 0;
                evaluateHands_v2(playerHands, nplayers, newBoard, stats, finalWeight, &winnersMask);
                // 增加具体手牌胜率统计（非同花分支）
                unsigned winnerCount = bitCount(winnersMask);
                double winShare = winnerCount > 0 ? 1.0 / winnerCount : 0.0;
                for (unsigned j = 0; j < nplayers; ++j) {
                    // 标准化手牌顺序
                    std::array<uint8_t, 2> normalizedHand = originalPlayerHands[j].cards;
                    if (normalizedHand[0] > normalizedHand[1]) {
                        std::swap(normalizedHand[0], normalizedHand[1]);
                    }
                    const std::string& key = CardRange::handToString(originalPlayerHands[j].cards);
                    stats->typeCounts[j][key] += finalWeight;
                    if (winnersMask & (1 << j)) {
                        stats->typeWins[j][key] += winShare * finalWeight;
                    }
                }
            }
        } else {
            unsigned lastRank = ~0;
            for (unsigned i = start; i < ndeck; ++i) {
                unsigned multiplier = 1;
                if (suitCounts[deck[i] & 3] < 4) {
                    unsigned rank = deck[i] >> 2;
                    if (rank == lastRank) continue;
                    for (unsigned j = i + 1; j < ndeck && deck[j] >> 2 == rank; ++j) {
                        if (suitCounts[deck[j] & 3] < 4)
                            ++multiplier;
                    }
                    lastRank = rank;
                }

                Hand newBoard = board + deck[i];
                double finalWeight = weight * multiplier;

                unsigned winnersMask = 0;
                evaluateHands_v2(playerHands, nplayers, newBoard, stats, finalWeight, &winnersMask);
                // 增加具体手牌胜率统计（同花分支）
                unsigned winnerCount = bitCount(winnersMask);
                double winShare = winnerCount > 0 ? 1.0 / winnerCount : 0.0;
                for (unsigned j = 0; j < nplayers; ++j) {
                    // 标准化手牌顺序
                    std::array<uint8_t, 2> normalizedHand = originalPlayerHands[j].cards;
                    if (normalizedHand[0] > normalizedHand[1]) {
                        std::swap(normalizedHand[0], normalizedHand[1]);
                    }
                    const std::string& key = CardRange::handToString(originalPlayerHands[j].cards);
                    stats->typeCounts[j][key] += finalWeight;
                    if (winnersMask & (1 << j)) {
                        stats->typeWins[j][key] += winShare * finalWeight;
                    }
                }
            }
        }
        return;
    }

    // 通用递归逻辑
    for (unsigned i = start; i < ndeck; ++i) {
        Hand newBoard = board;
        unsigned suit = deck[i] & 3;

        if (suitCounts[suit] + cardsLeft < 5) {
            unsigned irrelevantCount = 1;
            unsigned rank = deck[i] >> 2;

            for (unsigned j = i + 1; j < ndeck && deck[j] >> 2 == rank; ++j) {
                unsigned suit2 = deck[j] & 3;
                if (suitCounts[suit2] + cardsLeft < 5) {
                    if (j != i + irrelevantCount)
                        std::swap(deck[j], deck[i + irrelevantCount]);
                    ++irrelevantCount;
                }
            }

            for (unsigned repeats = 1; repeats <= std::min(irrelevantCount, cardsLeft); ++repeats) {
                static const unsigned BINOM_COEFF[5][5] = {{0}, {0, 1}, {1, 2, 1}, {1, 3, 3, 1}, {1, 4, 6, 4, 1}};
                unsigned newWeight = BINOM_COEFF[irrelevantCount][repeats];
                newBoard += deck[i + repeats - 1];
                
                if (repeats == cardsLeft) {
                double finalWeight = weight * newWeight;
                unsigned winnersMask = 0;
                evaluateHands_v2(playerHands, nplayers, newBoard, stats, finalWeight, &winnersMask);
                    
                    // 增加具体手牌胜率统计
                    unsigned winnerCount = bitCount(winnersMask);
                    double winShare = winnerCount > 0 ? 1.0 / winnerCount : 0.0;
                    for (unsigned j = 0; j < nplayers; ++j) {
                        const std::string& key = CardRange::handToString(originalPlayerHands[j].cards);
                        stats->typeCounts[j][key] += finalWeight;
                        if (winnersMask & (1 << j)) {
                            stats->typeWins[j][key] += winShare * finalWeight;
                        }
                    }
                }
                else
                    enumerateBoardWeightedRec(playerHands, nplayers, stats, newBoard, deck, ndeck, suitCounts,
                                  cardsLeft - repeats, i + irrelevantCount, weight * newWeight, originalPlayerHands);
            }

            i += irrelevantCount - 1;
        } else {
            newBoard += deck[i];
            ++suitCounts[suit];
            
            if (cardsLeft == 1) {
                double finalWeight = weight;
                unsigned winnersMask = 0;
                evaluateHands_v2(playerHands, nplayers, newBoard, stats, finalWeight, &winnersMask);
                
                // 增加具体手牌胜率统计
                unsigned winnerCount = bitCount(winnersMask);
                double winShare = winnerCount > 0 ? 1.0 / winnerCount : 0.0;
                for (unsigned j = 0; j < nplayers; ++j) {
                    const std::string& key = CardRange::handToString(originalPlayerHands[j].cards);
                    stats->typeCounts[j][key] += finalWeight;
                    if (winnersMask & (1 << j)) {
                        stats->typeWins[j][key] += winShare * finalWeight;
                    }
                }
            } else {
                enumerateBoardWeightedRec(playerHands, nplayers, stats, newBoard, deck, ndeck, suitCounts,
                              cardsLeft - 1, i + 1, weight, originalPlayerHands);
            }
            
            --suitCounts[suit];
        }
    }
}
// 加权常规蒙特卡洛模拟
/**
 * @brief 执行加权蒙特卡洛模拟，计算多玩家手牌范围的胜率 equity
 * @param[in] handRanges 加权手牌范围向量，每个元素代表一个玩家的所有可能手牌及对应权重
 * @param[in] boardCards 底牌掩码（64位整数，每bit代表一张牌，已发公共牌）
 * @param[in] deadCards 死牌掩码（64位整数，包含已弃牌和无法使用的牌）
 * @param[in] stdevTarget 目标标准差，用于控制模拟精度（达到该值时停止迭代）
 * 
 * @note 1. 权重计算采用乘积法则：总权重 = 各玩家手牌权重的乘积
 *       2. 模拟终止条件：达到最大迭代次数(maxEvaluations)或标准差低于目标值（但似乎不起作用，未解决）
 *       3. 内部使用BatchResults累积加权统计数据，包括胜率分布和牌型计数
 *       4. 线程安全设计，支持多线程并行模拟（通过mutex保护共享统计）
 */
void EquityCalculator::simulateWeightedRegularMonteCarlo(
    const std::vector<WeightedCardRange>& handRanges,
    uint64_t boardCards, uint64_t deadCards, double stdevTarget)
{
    std::lock_guard<std::mutex> lock(cout_mutex);
    // std::cout << "[Thread " << std::this_thread::get_id() << "] Starting simulation" << std::endl;
    // std::cout << "Hand ranges count: " << handRanges.size() << std::endl;
    for (const auto& range : handRanges) {
    // std::cout << "Combos size: " << range.getWeightedCombos().size() << std::endl;
}
    unsigned nplayers = (unsigned)handRanges.size();
    Hand fixedBoard = getBoardFromBitmask(boardCards);
    unsigned remainingCards = BOARD_CARDS - fixedBoard.count();
    BatchResults stats(nplayers);
    for (unsigned i = 0; i < nplayers; ++i) {
        stats.playerIds[i] = i;
    }

    Rng rng{std::random_device{}()};
    FastUniformIntDistribution<unsigned,16> cardDist(0, CARD_COUNT - 1);

    // double currentStdev = 0.0; // Unused variable
    unsigned evalCount = 0;
    const unsigned maxEvaluations = 100000000; // 增加迭代次数以提高准确性
    std::unordered_map<std::string, int> freq; // 用于统计抽样结果
    
    for (; evalCount < maxEvaluations; ++evalCount) {
        // 1. 按权重为每个玩家采样手牌，避免冲突
        uint64_t usedCardsMask = boardCards | deadCards;
        Hand playerHands[MAX_PLAYERS];
        std::array<std::array<uint8_t,2>, MAX_PLAYERS> sampledHands;
        bool ok = true;
        // 1. 按权重为每个玩家采样手牌，避免冲突
        for (unsigned i = 0; i < nplayers; ++i) {
            auto hand = handRanges[i].sample();
            std::string handStr = CardRange::handToString(hand);
            // std::cout << "Player " << i << ": " << handStr << std::endl;
            uint64_t mask = (1ull << hand[0]) | (1ull << hand[1]);  //创建手牌掩码
            if (usedCardsMask & mask) {
                ok = false;
                break;
            }
            playerHands[i] = Hand(hand);
            sampledHands[i] = hand;
            usedCardsMask |= mask;
            
            // 记录抽样结果
            freq[handStr]++;
        }
        
        if (!ok) {
            continue; // 重试
        }
        
        // 2. 计算单局联合权重
        double sumWeight = 1.0;
        bool weightValid = true;
        for (unsigned i = 0; i < nplayers; ++i) {
            double w = handRanges[i].weight(sampledHands[i]);
            if (w <= 1e-10) {
                weightValid = false;
                // std::cerr << "[ERROR] Player " << i << " has invalid hand weight: " << w << std::endl;
                break;
            }
            sumWeight *= w;
            // std::cout << "Player " << i << " hand weight: " << w << std::endl;
        }
        // std::cout << " Total weight: " << sumWeight << std::endl;
        
        if (!ok) continue;

        // 2. 随机发公共牌
        Hand board = fixedBoard;
        randomizeBoard(board, remainingCards, usedCardsMask, rng, cardDist);

        // 3. 评估胜负并累加权重
        unsigned winnersMask = 0;  // 初始化获胜者掩码（二进制位表示法）

        evaluateHands_v2(playerHands, nplayers, board, &stats, sumWeight, &winnersMask);

        // 统计手牌类型数据
        // 使用evaluateHands_v2计算好的winnersMask（二进制位标识获胜玩家）
        unsigned winnerCount = bitCount(winnersMask);  
        double winShare = winnerCount > 0 ? 1.0 / winnerCount : 0.0;

        // 用于统计各玩家每种具体手牌的胜率数据
        for (unsigned i = 0; i < nplayers; ++i) {
            // 输出的手牌和输入的类型一致，如输入76s,输出76s
            // std::string originalHand = CardRange::handToString(sampledHands[i]);
            // const std::string& handRangeStr = handRanges[i].getOriginalHandString(sampledHands[i]);
            // // 判断是否为具体牌型（长度为4的表示具体牌型，如7s6s）
            // bool isSpecificHand = (handRangeStr.length() == 4);
            // const std::string& key = isSpecificHand ? originalHand : handToType(sampledHands[i]);

            // sampledHands[i]输出两个手牌编码0-51，handToString转换成具体手牌字符串，如76s->7s6s,7d6d...
            const std::string& key = CardRange::handToString(sampledHands[i]); 
            stats.typeCounts[i][key] += sumWeight;
            if (winnersMask & (1 << i)) {
                stats.typeWins[i][key] += winShare * sumWeight;
            }
        }

        // 4. 定期更新
        if ((stats.evalCount & 0xfff) == 0) {
            updateResults(stats, false);
            stats = BatchResults(nplayers);
            // 查看标准差的输出
            // std::cout << "[Convergence] Calculated Stdev: " << mResults.stdev 
            //     << ", Target: " << stdevTarget << std::endl;
            // || mResults.stdev < stdevTarget去掉了标准差的限制，优化时可选用
            if (mStopped)
                break;
        }
    }
    // std::cout << "Calling updateResults with final stats" << std::endl;
    updateResults(stats, true);
    
}

/**
 * @brief 加权蒙特卡洛模拟中评估手牌并统计胜负权重（适用于带权重的手牌范围）
 * 
 * 该函数用于在加权蒙特卡洛模拟中，根据玩家手牌和公共牌评估各玩家的手牌强度，
 * 确定获胜玩家，并将带权重的结果累加到批次统计中。与普通版本的区别在于支持double类型权重，
 * 适用于非均匀概率分布的手牌范围场景。
 * 
 * @param[in] playerHands 玩家手牌数组，每个元素为一个玩家的手牌（包含两张底牌）
 * @param[in] nplayers 玩家数量（最多支持MAX_PLAYERS）
 * @param[in] board 公共牌组合（可能包含0-5张公共牌，根据当前游戏阶段动态生成）
 * @param[out] stats 批次统计结果对象，用于累积当前批次的胜负数据、手牌类型统计等
 * @param[in] weight 当前手牌组合的总权重（由各玩家手牌权重相乘得到，反映该组合的概率占比）
 * @param[out] winnersMask 获胜玩家掩码（二进制位表示，如0b01表示玩家0获胜）
 * 
 * @note 1. 权重需提前计算（各玩家手牌权重的乘积），且需大于1e-20以避免数值精度问题
 *       2. 获胜玩家掩码通过二进制位标识所有获胜玩家，用于后续计算平局时的权重分配
 *       3. 结果累加到stats->winsByPlayerMask_v2，供combineResults_v2合并到总结果
 */
void EquityCalculator::evaluateHands_v2(const Hand* playerHands, unsigned nplayers, const Hand& board, BatchResults* stats, double weight, unsigned* winnersMask)
{
    ++stats->evalCount;
    unsigned bestRank = 0;
    // 输入的weight为两个玩家一局中的牌型出现的联合概率（即totalweight）
    if (weight <= 1e-20) {
        // std::cerr << "[ERROR] Invalid weight in evaluateHands_v2: " << weight << std::endl;
        stats->skippedPreflopCombos++;
        return;
    }
    // 输出权重信息
    // std::cout << "[DEBUG] Valid weight in evaluateHands_v2: " << weight << std::endl;    
    for (unsigned i = 0, m = 1; i < nplayers; ++i, m <<= 1) {
        Hand hand = board + playerHands[i];
        unsigned rank = mEval.evaluate<true>(hand);
        if (rank > bestRank) {
            bestRank = rank;
            *winnersMask = m;
        } else if (rank == bestRank) {
            *winnersMask |= m;
        }
        // std::cout << "Player " << i << ": rank=" << rank << ", mask=" << *winnersMask << std::endl;
    }
    // std::cout << "[DEBUG] winnersMask: 0x" << std::hex << *winnersMask << std::dec 
            //   << ", weight: " << std::fixed << std::setprecision(10) << weight 
            //   << ", before: " << stats->winsByPlayerMask_v2[*winnersMask] << std::endl;
    
    stats->winsByPlayerMask_v2[*winnersMask] += weight;
    // 输出更新后的值
    // std::cout << "[DEBUG] after: " << stats->winsByPlayerMask_v2[*winnersMask] << std::endl;
    // stats->winsByPlayerMask_v2[*winnersMask].store(weight, std::memory_order_relaxed);
}

/**
 * @brief 合并加权批次结果到全局统计中（支持double类型权重）
 * 
 * 该函数用于加权蒙特卡洛模拟或全枚举模式下，将单次批次计算产生的统计结果（包含权重信息）
 * 合并到全局结果集。与普通版本的区别在于支持double类型权重值，适用于非均匀概率分布场景。
 * 
 * @param[in] batch 批次结果结构体，包含当前批次的玩家掩码胜率、牌型统计等带权重的统计数据
 * @return double 批次 equity 值（用于标准差计算）
 * @note 1. 内部会累加 batch 中的 winsByPlayerMask_v2 到全局 results.winsByPlayerMask_v2
 * @note 2. 自动合并牌型统计数据（typeCounts 和 typeWins）到全局结果
 * @note 3. 权重总和累加到 results.totalAllSum，权重平方和累加到 results.totalWinSqrSum
 * @note 4. 该函数线程安全，通过 mutex 保护全局结果的写操作
 */
double EquityCalculator::combineResults_v2(const BatchResults& batch) {
    // 调试是否调用函数
    // std::cout << "combineResults_v2 called" << std::endl;

    // 合并同类型统计数据
    for (unsigned i = 0; i < batch.typeCounts.size(); ++i) {
        for (const auto& pair : batch.typeCounts[i]) {
            const auto& type = pair.first;
            const auto& count = pair.second;
            mResults.typeCounts[i][type] += count;
        }
        for (const auto& pair : batch.typeWins[i]) {
            const auto& type = pair.first;
            const auto& win = pair.second;
            mResults.typeWins[i][type] += win;
        }
    }
    // if (!batch.typeCounts.empty()) {    // 确保 batch 中有 Player 0 的数据
    //     for (const auto& [type, count] : batch.typeCounts[0]) {
    //         std::cout << "Player 0 type " << type 
    //                   << " added: " << count 
    //                   << " total: " << mResults.typeCounts[0][type] << std::endl;
    //     }
    // }
    // 本批次所有玩家全部模拟对局的累计总权重
    double totalWeight = 0.0;

    // 本批次每个玩家的加权获胜权重总和（包括平局平分权重）
    std::vector<double> playerWeights(mResults.players, 0.0);
    
    // 本批次平局权重与平方和计算
    double winSqrSum = 0.0;
    double totalTieWeight = 0;

    // 计算当前批次所有掩码类型相加的总权重（包括平局平分权重），存入对应playerWeights[i]
    for (unsigned mask = 0; mask < (1u << mResults.players); ++mask) {
        double weight = batch.winsByPlayerMask_v2[mask];
        // 当前批次累计权重总和和winSqrSum
        totalWeight += weight;
        winSqrSum += weight * weight;
        unsigned winnerCount = bitCount(mask);
        // 累加平局权重（赢家数>1即为平局）
        if (winnerCount > 1) {
            totalTieWeight += weight;
        }
        for (unsigned j = 0; j < mResults.players; ++j) {
            if (mask & (1 << j)) {
                playerWeights[j] += weight / winnerCount;
            }
        }
    }
    // 将平局权重累加到结果结构体
    mResults.weightedTies += totalTieWeight;
    // 累积全局掩码权重总和与平方和
    mResults.totalAllSum += totalWeight;
    mResults.totalWinSqrSum += winSqrSum;
    
    // 输出当前线程ID、批次权重、更新后的全局总权重
    // std::cout << "Thread " << std::this_thread::get_id() 
    //           << " batch weight: " << totalWeight 
    //           << " global total: " << mResults.totalAllSum << std::endl;

    // 更新结果
    mResults.intervalHands = totalWeight;
    for (unsigned i = 0; i < mResults.players; ++i) {
        // 多线程累加玩家权重
        mResults.wins[i] += playerWeights[i];
        if (mResults.totalAllSum > 1e-20) {
            mResults.equity[i] = mResults.wins[i] / mResults.totalAllSum;
        } else {
            mResults.equity[i] = 0.0;
        }
        // mResults.ties += winByPlayerMask[3];  
        // std::cout << "Player " << i << " win: " << mResults.wins[i] 
        //           << " equity: " << mResults.equity[i] << std::endl;
    }

    mResults.evaluations += batch.evalCount;
    mResults.skippedPreflopCombos += batch.skippedPreflopCombos;
    mResults.evaluatedPreflopCombos += batch.uniquePreflopCombos;
    
    // 基于累积值计算标准差
    if (mResults.totalAllSum > 1e-9) {
        // double mean = mResults.totalWinSum / mResults.totalAllSum;
        // double variance = (mResults.totalWinSqrSum / mResults.totalAllSum)- mean * mean;
        // mResults.stdev = std::sqrt(variance);
        double sumWeights = mResults.totalAllSum;
        // double mean = sumWeights / sumWeights; // Unused variable权重总和的均值为1.0
        double variance = (mResults.totalWinSqrSum - sumWeights) / sumWeights;
        mResults.stdev = sqrt(variance);
    } else {
        mResults.stdev = 0;
    }
    return mResults.equity[0]; // 返回第一个玩家的equity
}

/**
 * calculateIndividualEquities计算每个玩家的每种具体牌型的胜率（如45s、AA等）
 * @param handRanges 玩家手牌范围列表（含权重，如"45s:1.0, ATo:0.6"）
 * @param boardCards 公共牌的位掩码表示（标记已发出的公共牌）
 * @param deadCards 死牌的位掩码表示（标记需排除的牌）
 * @param stdevTarget 标准差目标阈值（控制模拟精度，小于该值时停止）
 * @param updateInterval 结果更新间隔（秒，用于进度回调）
 * @param threadCount 并行计算的线程数量（控制模拟效率）
 * @return 每个玩家的牌型-胜率映射（如{ "45s":0.71, "AA":0.85 }）
 */

std::vector<std::unordered_map<std::string, double>> EquityCalculator::getTypeEquities() const
{
    std::lock_guard<std::mutex> lock(mMutex); // 确保线程安全
    std::vector<std::unordered_map<std::string, double>> result;
    result.reserve(mResults.players);

    for (unsigned i = 0; i < mResults.players; ++i) {
            std::unordered_map<std::string, double> typeEquity;
            // 遍历该玩家的所有牌型
            for (const auto& countPair : mResults.typeCounts[i]) {
                const std::string& type = countPair.first;
                double total = countPair.second;
                // 移除过滤条件，确保所有手牌组合都被输出

                // 查找该牌型的获胜权重（若未记录则为0）
                double wins = 0.0;
                auto winIt = mResults.typeWins[i].find(type);
                if (winIt != mResults.typeWins[i].end()) {
                    wins = winIt->second;
                }
                
                // 调试输出：查看typeCounts和typeWins中的值
                if (type.find("Kh2") != std::string::npos) {
                    std::cout << "DEBUG: getTypeEquities - Player " << i << ": " << type
                              << " wins=" << wins << " total=" << total
                              << " equity=" << (wins > 0 || total <= 1e-20 ? wins / total : 1e-9) << std::endl;
                }
            // 计算胜率（获胜权重 / 总出现权重）
            double equity = (wins > 0 || total <= 1e-20) ? (wins / total) : 1e-9;
            // 确保即使很小的胜率也不会被四舍五入为0
            typeEquity[type] = equity;
        }
        result.push_back(typeEquity);
    }
    return result;
}

// 将手牌转换为类型，如7s6s,7c6c,7h6h,7d6d-->76s
std::string EquityCalculator::handToType(const std::array<uint8_t, 2>& hand,bool preserveSpecific) const {
    static const char* RANK_CHARS = "23456789TJQKA";
    static const std::array<char, 14> rankMap = [](){ // 索引0-13对应2-A
        std::array<char, 14> arr; 
        for(int i=0; i<14; ++i) arr[i] = RANK_CHARS[i];
        return arr; 
    }();

    // 输入验证
    if (hand[0] >= 52 || hand[1] >= 52) {
        throw std::invalid_argument("Invalid card index");
    }
    if (preserveSpecific) {
        // 确保卡牌顺序与CardRange::addCombo一致
        uint8_t c1 = hand[0];
        uint8_t c2 = hand[1];
        uint8_t r1 = c1 / 4;
        uint8_t r2 = c2 / 4;
        
        // 调试输出：检查Kh2系列手牌的输入
        if (((r1 == 11 && r2 == 0) || (r1 == 0 && r2 == 11))) {
            std::cerr << "[H2TYPE] Input hand (preserveSpecific): " 
                      << RANK_CHARS[r1] << ((c1 % 4 == 0) ? 's' : (c1 % 4 == 1) ? 'h' : (c1 % 4 == 2) ? 'c' : 'd')
                      << RANK_CHARS[r2] << ((c2 % 4 == 0) ? 's' : (c2 % 4 == 1) ? 'h' : (c2 % 4 == 2) ? 'c' : 'd') 
                      << " (c1: " << (int)c1 << ", c2: " << (int)c2 << ", r1: " << (int)r1 << ", r2: " << (int)r2 << ", suits: " << (c1%4) << "," << (c2%4) << ")" << std::endl;
        }
        
        // 点数大的在前，如果点数相同则花色小的在前
        if ((c1 >> 2) < (c2 >> 2) || 
            ((c1 >> 2) == (c2 >> 2) && (c1 & 3) < (c2 & 3))) {
            std::swap(c1, c2);
        }
        
        // 调试输出：检查排序后的手牌
        r1 = c1 / 4;
        r2 = c2 / 4;
        if (((r1 == 11 && r2 == 0) || (r1 == 0 && r2 == 11))) {
            std::cerr << "[H2TYPE] Ordered hand (preserveSpecific): " 
                      << RANK_CHARS[r1] << ((c1 % 4 == 0) ? 's' : (c1 % 4 == 1) ? 'h' : (c1 % 4 == 2) ? 'c' : 'd')
                      << RANK_CHARS[r2] << ((c2 % 4 == 0) ? 's' : (c2 % 4 == 1) ? 'h' : (c2 % 4 == 2) ? 'c' : 'd') << std::endl;
        }
        
        std::array<uint8_t, 2> orderedHand = {c1, c2};
        std::string result = CardRange::handToString(orderedHand);
        
        // 调试输出：检查最终结果
        if (((r1 == 11 && r2 == 0) || (r1 == 0 && r2 == 11))) {
            std::cerr << "[H2TYPE] Final result (preserveSpecific): " << result << std::endl;
            
            // 额外调试：显示卡牌的花色信息
            char suit1 = (c1 % 4 == 0) ? 's' : (c1 % 4 == 1) ? 'h' : (c1 % 4 == 2) ? 'c' : 'd';
            char suit2 = (c2 % 4 == 0) ? 's' : (c2 % 4 == 1) ? 'h' : (c2 % 4 == 2) ? 'c' : 'd';
            std::cerr << "[H2TYPE] Cards info - K" << suit1 << "2" << suit2 << " vs result: " << result << std::endl;
        }
        
        return result;
    }
    uint8_t r1 = hand[0] / 4;
    uint8_t r2 = hand[1] / 4;
    bool isSuited = (hand[0] % 4) == (hand[1] % 4);

    if (r1 < r2) std::swap(r1, r2);

    std::string result;
    if (r1 == r2) {
        result = std::string(2, rankMap[r1]); // 对子（如AA）
    } else {
        char buf[4];
        snprintf(buf, sizeof(buf), "%c%c%c", rankMap[r1], rankMap[r2], isSuited ? 's' : 'o');
        result = std::string(buf);
    }
    
    // 调试输出：检查Kh2系列手牌
    if (((r1 == 11 && r2 == 0) || (r1 == 0 && r2 == 11))) {
        std::cerr << "[H2TYPE] K2 type: " << result << " (preserveSpecific: " << (preserveSpecific ? "true" : "false") << ")" << std::endl;
    }
    
    return result;
}


// 原始函数
bool EquityCalculator::start(const std::vector<CardRange>& handRanges, uint64_t boardCards, uint64_t deadCards,
                             bool enumerateAll, double stdevTarget, std::function<void(const Results&)> callback,
                             double updateInterval, unsigned threadCount)
{
    mResults.isRegularMonteCarlo = true;
    if (handRanges.size() == 0 || handRanges.size() > MAX_PLAYERS)
        return false;
    if (bitCount(boardCards) > BOARD_CARDS)
        return false;
    if (2 * handRanges.size() + bitCount(deadCards) + BOARD_CARDS > CARD_COUNT)
        return false;

    // Set up card ranges.
    mDeadCards = deadCards;
    mBoardCards = boardCards;
    mOriginalCardRanges = handRanges;
    mHandRanges = removeInvalidCombos(handRanges, mDeadCards | mBoardCards);
    std::vector<CombinedRange> combinedRanges = CombinedRange::joinRanges(mHandRanges, MAX_COMBINED_RANGE_SIZE);
    for (unsigned i = 0; i < combinedRanges.size(); ++i) {
        if (combinedRanges[i].combos().size() == 0)
            return false;
        if (!enumerateAll)
            combinedRanges[i].shuffle();
        mCombinedRanges[i] = combinedRanges[i];
    }
    mCombinedRangeCount = (unsigned)combinedRanges.size();

    // Set up simulation settings.
    mEnumPosition = 0;
    mBatchSum = mBatchSumSqr = mBatchCount = 0;
    mResults = Results();
    mResults.players = (unsigned)handRanges.size();
    mResults.enumerateAll = enumerateAll;
    mUpdateResults = mResults;
    mStdevTarget = stdevTarget;
    mCallback = callback;
    mUpdateInterval = updateInterval;
    mStopped = false;
    mLastUpdate = std::chrono::high_resolution_clock::now();
    if (threadCount == 0)
        threadCount = std::thread::hardware_concurrency();
    mUnfinishedThreads = threadCount;

    // Start threads.
    mThreads.clear();
    for (unsigned i = 0; i < threadCount; ++i) {
        mThreads.emplace_back([this,enumerateAll]{
            if (enumerateAll)
                enumerate();
            else
                simulateRandomWalkMonteCarlo();
        });
    }

    // Started successfully.
    return true;
}

// Regular monte carlo simulation.
void EquityCalculator::simulateRegularMonteCarlo()
{
    unsigned nplayers = (unsigned)mHandRanges.size();
    Hand fixedBoard = getBoardFromBitmask(mBoardCards);
    unsigned remainingCards = BOARD_CARDS - fixedBoard.count();
    BatchResults stats(nplayers);

    Rng rng{std::random_device{}()};
    FastUniformIntDistribution<unsigned,16> cardDist(0, CARD_COUNT - 1);
    FastUniformIntDistribution<unsigned,21> comboDists[MAX_PLAYERS];
    unsigned combinedRangeCount = mCombinedRangeCount;
    for (unsigned i = 0; i < mCombinedRangeCount; ++i)
        comboDists[i] = FastUniformIntDistribution<unsigned,21>(0, (unsigned)mCombinedRanges[i].combos().size() - 1);

    for (;;) {
        // Randomize hands and check for duplicate holecards.
        uint64_t usedCardsMask = 0;
        Hand playerHands[MAX_PLAYERS];
        bool ok = true;
        for (unsigned i = 0; i < combinedRangeCount; ++i) {
            unsigned comboIdx = comboDists[i](rng);
            const CombinedRange::Combo& combo = mCombinedRanges[i].combos()[comboIdx];
            if (usedCardsMask & combo.cardMask) {
                ok = false;
                break;
            }
            for (unsigned j = 0; j < mCombinedRanges[i].playerCount(); ++j) {
                unsigned playerIdx = mCombinedRanges[i].players()[j];
                playerHands[playerIdx] = combo.evalHands[j];
            }
            usedCardsMask |= combo.cardMask;
        }

        // Conflicting holecards, try again.
        if (!ok) {
            if (++stats.skippedPreflopCombos > 1000 && stats.evalCount == 0) {
                break;
            } continue;
        }

        Hand board = fixedBoard;
        randomizeBoard(board, remainingCards, usedCardsMask | mDeadCards | mBoardCards, rng, cardDist);
        evaluateHands(playerHands, nplayers, board, &stats, 1);

        // Update periodically.
        if ((stats.evalCount & 0xfff) == 0) {
            updateResults(stats, false);
            stats = BatchResults(nplayers);
            if (mStopped)
                break;
        }
    }

    updateResults(stats, true);
}

// Monte carlo simulation using a random walk. On each iteration a random player is chosen and the next feasible
// combo is picked for that player.
void EquityCalculator::simulateRandomWalkMonteCarlo()
{
    unsigned nplayers = (unsigned)mHandRanges.size();
    Hand fixedBoard = getBoardFromBitmask(mBoardCards);
    unsigned remainingCards = 5 - fixedBoard.count();
    BatchResults stats(nplayers);

    Rng rng{std::random_device{}()};
    FastUniformIntDistribution<unsigned,16> cardDist(0, CARD_COUNT - 1);
    FastUniformIntDistribution<unsigned,21> comboDists[MAX_PLAYERS];
    FastUniformIntDistribution<unsigned,16> combinedRangeDist(0, mCombinedRangeCount - 1);
    for (unsigned i = 0; i < mCombinedRangeCount; ++i)
        comboDists[i] = FastUniformIntDistribution<unsigned,21>(0, (unsigned)mCombinedRanges[i].combos().size() - 1);

    uint64_t usedCardsMask;
    Hand playerHands[MAX_PLAYERS];
    unsigned comboIndexes[MAX_PLAYERS];

    // Set initial state.
    if (randomizeHoleCards(usedCardsMask, comboIndexes, playerHands, rng, comboDists)) {
        // Loop until stopped.
        for (;;) {
            // Randomize board and evaluate for current holecards.
            Hand board = fixedBoard;
            randomizeBoard(board, remainingCards, usedCardsMask, rng, cardDist);
            evaluateHands(playerHands, nplayers, board, &stats, 1);

            // Update results periodically.
            if ((stats.evalCount & 0xfff) == 0) {
                updateResults(stats, false);
                if (mStopped)
                    break;
                stats = BatchResults(nplayers);
                if (!randomizeHoleCards(usedCardsMask, comboIndexes, playerHands, rng, comboDists))
                    break;
            }

            // Choose random player and iterate to next valid combo.
            unsigned combinedRangeIdx = combinedRangeDist(rng);
            const CombinedRange& combinedRange = mCombinedRanges[combinedRangeIdx];
            unsigned comboIdx = comboIndexes[combinedRangeIdx];
            usedCardsMask -= combinedRange.combos()[comboIdx].cardMask;
            uint64_t mask = 0;
            do {
                if (comboIdx == 0)
                    comboIdx = (unsigned)combinedRange.size();
                --comboIdx;
                mask = combinedRange.combos()[comboIdx].cardMask;
            } while (mask & usedCardsMask);
            usedCardsMask |= mask;
            for (unsigned i = 0; i < combinedRange.playerCount(); ++i) {
                unsigned playerIdx = combinedRange.players()[i];
                playerHands[playerIdx] = combinedRange.combos()[comboIdx].evalHands[i];
            }
            comboIndexes[combinedRangeIdx] = comboIdx;
        }
    }

    updateResults(stats, true);
}

// Randomize holecards using rejection sampling. Returns false if maximum number of attempts was reached.
bool EquityCalculator::randomizeHoleCards(uint64_t &usedCardsMask, unsigned* comboIndexes, Hand* playerHands,
                                          Rng& rng, FastUniformIntDistribution<unsigned,21>* comboDists)
{
    unsigned n = 0;
    for(bool ok = false; !ok && n < 1000; ++n) {
        ok = true;
        usedCardsMask = mDeadCards | mBoardCards;
        for (unsigned i = 0; i < mCombinedRangeCount; ++i) {
            unsigned comboIdx = comboDists[i](rng);
            comboIndexes[i] = comboIdx;
            const CombinedRange::Combo& combo = mCombinedRanges[i].combos()[comboIdx];
            if (usedCardsMask & combo.cardMask) {
                ok = false;
                break;
            }
            for (unsigned j = 0; j < mCombinedRanges[i].playerCount(); ++j) {
                unsigned playerIdx = mCombinedRanges[i].players()[j];
                playerHands[playerIdx] = combo.evalHands[j];
            }
            usedCardsMask |= combo.cardMask;
        }
    }
    return n < 1000;
}

// Naive method of randomizing the board by using rejection sampling.
void EquityCalculator::randomizeBoard(Hand& board, unsigned remainingCards, uint64_t usedCardsMask,
                                      Rng& rng, FastUniformIntDistribution<unsigned,16>& cardDist)
{
    omp_assert(remainingCards + bitCount(usedCardsMask) <= CARD_COUNT && remainingCards <= BOARD_CARDS);
    for(unsigned i = 0; i < remainingCards; ++i) {
        unsigned card;
        uint64_t cardMask;
        do {
            card = cardDist(rng);
            cardMask = 1ull << card;
        } while (usedCardsMask & cardMask);
        usedCardsMask |= cardMask;
        board += Hand(card);
    }
}

// Evaluates a single showdown with one or more players and stores the result.
template<bool tFlushPossible>
void EquityCalculator::evaluateHands(const Hand* playerHands, unsigned nplayers, const Hand& board, BatchResults* stats,
                                     unsigned weight)
{
    omp_assert(board.count() == BOARD_CARDS);
    ++stats->evalCount;
    unsigned bestRank = 0;
    unsigned winnersMask = 0;
    for (unsigned i = 0, m = 1; i < nplayers; ++i, m <<= 1) {
        Hand hand = board + playerHands[i];
        unsigned rank = mEval.evaluate<tFlushPossible>(hand);
        if (rank > bestRank) {
            bestRank = rank;
            winnersMask = m;
        } else if (rank == bestRank) {
            winnersMask |= m;
        }
    }

    stats->winsByPlayerMask[winnersMask] += weight;
}

// Calculates exact equities by enumerating through all possible combinations.
void EquityCalculator::enumerate()
{
    uint64_t enumPosition = 0, enumEnd = 0;
    uint64_t preflopCombos = getPreflopCombinationCount();
    unsigned nplayers = (unsigned)mHandRanges.size();
    BatchResults stats(nplayers);
    UniqueRng64 urng(preflopCombos);
    Hand fixedBoard = getBoardFromBitmask(mBoardCards);
    libdivide::libdivide_u64_t fastDividers[MAX_PLAYERS];
    unsigned combinedRangeCount = mCombinedRangeCount;
    for (unsigned i = 0; i < combinedRangeCount; ++i)
        fastDividers[i] = libdivide::libdivide_u64_gen(mCombinedRanges[i].combos().size());

    // Lookup overhead becomes too much if postflop tree is very small.
    uint64_t postflopCombos = getPostflopCombinationCount();
    bool useLookup = postflopCombos > 500;

    // Disable random preflop enumeration order if postflop is too small (bad for caching). It's also makes no sense
    // if all the combos don't fit in the lookup table.
    bool randomizeOrder = postflopCombos > 10000 && preflopCombos <= 2 * MAX_LOOKUP_SIZE;

    for (;;++enumPosition) {
        // Ask for more work if we don't have any.
        if (enumPosition >= enumEnd) {
            uint64_t batchSize = std::max<uint64_t>(2000000 / postflopCombos, 1);
            std::tie(enumPosition, enumEnd) = reserveBatch(batchSize);
            if (enumPosition >= enumEnd)
                break;
        }

        // Use a quasi-RNG to randomize the preflop enumeration order, while still making sure
        // every combo is evaluated once.
        uint64_t randomizedEnumPos = randomizeOrder ? urng(enumPosition) : enumPosition;

        // Map enumeration index to actual hands and check duplicate card.
        bool ok = true;
        uint64_t usedCardsMask = mBoardCards | mDeadCards;
        HandWithPlayerIdx playerHands[MAX_PLAYERS];
        for (unsigned i = 0; i < combinedRangeCount; ++i) {
            uint64_t quotient = libdivide_u64_do(randomizedEnumPos, &fastDividers[i]);
            uint64_t remainder = randomizedEnumPos - quotient * mCombinedRanges[i].combos().size();
            randomizedEnumPos = quotient;

            const CombinedRange::Combo& combo = mCombinedRanges[i].combos()[(size_t)remainder];
            if (usedCardsMask & combo.cardMask) {
                ok = false;
                break;
            }
            usedCardsMask |= combo.cardMask;
            for (unsigned j = 0; j < mCombinedRanges[i].playerCount(); ++j) {
                unsigned playerIdx = mCombinedRanges[i].players()[j];
                playerHands[playerIdx].cards = combo.holeCards[j];
                playerHands[playerIdx].playerIdx = playerIdx;
            }
        }

        if(!ok) {
            ++stats.skippedPreflopCombos; //TODO fix skipcount
        } else {
            // Transform preflop into canonical form so that suit and player isomoprhism can be detected.
            uint64_t boardCards = mBoardCards;
            uint64_t deadCards = mDeadCards;
            if (useLookup) {
                // Sort players based on their hand.
                std::sort(playerHands, playerHands + nplayers, [](const HandWithPlayerIdx& lhs,
                          const HandWithPlayerIdx& rhs){
                    if (lhs.cards[0] >> 2 != rhs.cards[0] >> 2)
                        return lhs.cards[0] >> 2 < rhs.cards[0] >> 2;
                    if (lhs.cards[1] >> 2 != rhs.cards[1] >> 2)
                        return lhs.cards[1] >> 2 < rhs.cards[1] >> 2;
                    if ((lhs.cards[0] & 3) != (rhs.cards[0] & 3))
                        return (lhs.cards[0] & 3) < (rhs.cards[0] & 3);
                    return (lhs.cards[1] & 3) < (rhs.cards[1] & 3);
                });

                // Save original player indexes cause we eventually want the results for the original order.
                for (unsigned i = 0; i < nplayers; ++i)
                    stats.playerIds[i] = playerHands[i].playerIdx;

                // Suit isomorphism.
                transformSuits(playerHands, nplayers, &boardCards, &deadCards);
                usedCardsMask = boardCards | deadCards;
                for (unsigned j = 0; j < nplayers; ++j)
                    usedCardsMask |= (1ull << playerHands[j].cards[0]) | (1ull << playerHands[j].cards[1]);

                // Get cached results if this combo has already been calculated.
                uint64_t preflopId = calculateUniquePreflopId(playerHands, nplayers);
                if (lookupResults(preflopId, stats)) {
                    for (unsigned i = 0; i < nplayers; ++i)
                        stats.playerIds[i] = playerHands[i].playerIdx;
                    stats.evalCount = 0;
                    stats.uniquePreflopCombos = 0;
                } else {
                    // Do full postflop enumeration.
                    ++stats.uniquePreflopCombos;
                    Hand board = getBoardFromBitmask(boardCards);
                    enumerateBoard(playerHands, nplayers, board, usedCardsMask, &stats);
                    storeResults(preflopId, stats);
                }
            } else {
                ++stats.uniquePreflopCombos;
                enumerateBoard(playerHands, nplayers, fixedBoard, usedCardsMask, &stats);
            }
        }

        //TODO combine lookup results here so we don't need update so often
        if (stats.evalCount >= 10000 || stats.skippedPreflopCombos >= 10000 || useLookup) {
            updateResults(stats, false);
            stats = BatchResults(nplayers);
            if (mStopped)
                break;
        }
    }

    updateResults(stats, true);
}

// Starts the postflop enumeration.
void EquityCalculator::enumerateBoard(const HandWithPlayerIdx* playerHands, unsigned nplayers,
                                 const Hand& board, uint64_t usedCardsMask, BatchResults* stats)
{
    Hand hands[MAX_PLAYERS];
    for (unsigned i = 0; i < nplayers; ++i)
        hands[i] = Hand(playerHands[i].cards);

    // Take a shortcut when no board cards left to iterate.
    unsigned remainingCards = BOARD_CARDS - board.count();
    if (remainingCards == 0) {
        unsigned winnersMask = 0;
        evaluateHands<true>(hands, nplayers, board, stats, 1);
        
        // Calculate winners mask from winsByPlayerMask
        double maxWins = 0;
        for (unsigned mask = 1; mask < (1u << nplayers); ++mask) {
            if (stats->winsByPlayerMask[mask] > maxWins) {
                maxWins = stats->winsByPlayerMask[mask];
                winnersMask = mask;
            }
        }
        
        // Record specific hand statistics for each player
        for (unsigned j = 0; j < nplayers; ++j) {
            std::string key = CardRange::handToString(playerHands[j].cards);
            stats->typeCounts[j][key] += 1;
            
            if (winnersMask & (1u << j)) {
                unsigned winnerCount = __builtin_popcount(winnersMask);
                double winShare = 1.0 / winnerCount;
                stats->typeWins[j][key] += winShare;
            }
        }
        return;
    }

    // Initialize deck. This also determines the enumeration order. Iterating ranks in descending order is ~5%
    // faster for some reason. Could be better branch prediction, because lower cards affect hand value less. It's
    // unlikely to be due to caching, because reversing the evaluator's rank multipliers has no effect.
    unsigned deck[CARD_COUNT];
    unsigned ndeck = 0;
    for (unsigned c = CARD_COUNT; c-- > 0;) {
        if(!(usedCardsMask & (1ull << c)))
            deck[ndeck++] = c;
    }

    // Calculate the maximum card count for each suit that any player can have after holecards and fixed board cards.
    unsigned suitCounts[SUIT_COUNT] = {};
    for (unsigned i = 0; i < nplayers; ++i) {
        if ((playerHands[i].cards[0] & 3) == (playerHands[i].cards[1] & 3)) {
            suitCounts[playerHands[i].cards[0] & 3] = std::max(2u, suitCounts[playerHands[i].cards[0] & 3]);
        } else {
            suitCounts[playerHands[i].cards[0] & 3] = std::max(1u, suitCounts[playerHands[i].cards[0] & 3]);
            suitCounts[playerHands[i].cards[1] & 3] = std::max(1u, suitCounts[playerHands[i].cards[1] & 3]);
        }
    }
    for (unsigned i = 0; i < SUIT_COUNT; ++i)
        suitCounts[i] += board.suitCount(i);

    enumerateBoardRec(hands, nplayers, stats, board, deck, ndeck, suitCounts, remainingCards, 0, 1, playerHands);
}

// Enumerates board cards recursively. Detects some isomorphic subtrees by looking at the number of cards for
// each suit. Suits that cannot create a flush anymore (called here "irrelevant suits") are handled at the same time,
// which gives roughly a speedup of 3x.
void EquityCalculator::enumerateBoardRec(const Hand* playerHands, unsigned nplayers, BatchResults* stats,
                                const Hand& board, unsigned* deck, unsigned ndeck, unsigned* suitCounts,
                                unsigned cardsLeft, unsigned start, unsigned weight, const HandWithPlayerIdx* originalPlayerHands)
{
    // More efficient version for the innermost loop.
    if (cardsLeft == 1)
    {
        // Even simpler version for non-flush rivers.
        if (suitCounts[0] < 4 && suitCounts[1] < 4 && suitCounts[2] < 4 && suitCounts[3] < 4) {
            for (unsigned i = start; i < ndeck; ) {
                unsigned multiplier = 1;

                Hand newBoard = board + deck[i];

                // Count how many cards there are with same rank.
                unsigned rank = deck[i] >> 2;
                for (++i; i < ndeck && deck[i] >> 2 == rank; ++i)
                    ++multiplier;

                // 评估手牌并获取获胜者掩码
                unsigned winnersMask = 0;
                unsigned bestRank = 0;
                for (unsigned j = 0, m = 1; j < nplayers; ++j, m <<= 1) {
                    Hand hand = newBoard + playerHands[j];
                    unsigned rank = mEval.evaluate<false>(hand);
                    if (rank > bestRank) {
                        bestRank = rank;
                        winnersMask = m;
                    } else if (rank == bestRank) {
                        winnersMask |= m;
                    }
                }
                
                // 更新全局统计
                stats->winsByPlayerMask[winnersMask] += multiplier * weight;
                
                // 使用原始手牌信息记录typeCounts和typeWins
                if (originalPlayerHands != nullptr) {
                    for (unsigned j = 0; j < nplayers; ++j) {
                        // 使用handToType生成与getTypeEquities一致的键
                        std::string key = handToType(originalPlayerHands[j].cards, true);
                        stats->typeCounts[j][key] += multiplier * weight;
                        
                        if (winnersMask & (1u << j)) {
                            unsigned winnerCount = bitCount(winnersMask);
                            double winShare = winnerCount > 0 ? (double)(multiplier * weight) / winnerCount : 0.0;
                            stats->typeWins[j][key] += winShare;
                        }
                    }
                }
                

            }
        } else {
            unsigned lastRank = ~0;
            for (unsigned i = start; i < ndeck; ++i) {
                unsigned multiplier = 1;

                if (suitCounts[deck[i] & 3] < 4) {
                    unsigned rank = deck[i] >> 2;
                    if (rank == lastRank)
                        continue;
                    // Since this is last card there's no need to do reorder deck cards; we just count the
                    // irrelevant suits in current rank.
                    for (unsigned j = i + 1; j < ndeck && deck[j] >> 2 == rank; ++j) {
                        if (suitCounts[deck[j] & 3] < 4)
                            ++multiplier;
                    }
                    lastRank = rank;
                }

                Hand newBoard = board + deck[i];
                
                // 评估手牌并获取获胜者掩码
                unsigned winnersMask = 0;
                unsigned bestRank = 0;
                for (unsigned j = 0, m = 1; j < nplayers; ++j, m <<= 1) {
                    Hand hand = newBoard + playerHands[j];
                    unsigned rank = mEval.evaluate<true>(hand);
                    if (rank > bestRank) {
                        bestRank = rank;
                        winnersMask = m;
                    } else if (rank == bestRank) {
                        winnersMask |= m;
                    }
                }
                
                // 更新全局统计
                stats->winsByPlayerMask[winnersMask] += multiplier * weight;
            }
        }
        return;
    }

    // General version.
    for (unsigned i = start; i < ndeck; ++i) {
        Hand newBoard = board;

        unsigned suit = deck[i] & 3;

        if (suitCounts[suit] + cardsLeft < 5) {
            unsigned irrelevantCount = 1;
            unsigned rank = deck[i] >> 2;

            // Go through all the cards with same rank (they're always consecutive) and count the irrelevat suits.
            for (unsigned j = i + 1; j < ndeck && deck[j] >> 2 == rank; ++j) {
                unsigned suit2 = deck[j] & 3;
                if (suitCounts[suit2] + cardsLeft < 5) {
                    // Move all the irrelevant suits before other suits so they don't get used again.
                    if (j != i + irrelevantCount)
                        std::swap(deck[j], deck[i + irrelevantCount]);
                    ++irrelevantCount;
                }
            }

            // When there are multiple cards with irrelevant suits we have to choose how many of them to use,
            // and the number of isomorphic subtrees depends on it.
            for (unsigned repeats = 1; repeats <= std::min(irrelevantCount, cardsLeft); ++repeats) {
                static const unsigned BINOM_COEFF[5][5] = {{0}, {0, 1}, {1, 2, 1}, {1, 3, 3, 1}, {1, 4, 6, 4, 1}};
                unsigned newWeight = BINOM_COEFF[irrelevantCount][repeats] * weight;
                newBoard += deck[i + repeats - 1];
                if (repeats == cardsLeft) {
                        // 直接评估手牌并记录统计信息
                        unsigned winnersMask = 0;
                        unsigned bestRank = 0;
                        for (unsigned j = 0, m = 1; j < nplayers; ++j, m <<= 1) {
                            Hand hand = newBoard + playerHands[j];
                            unsigned rank = mEval.evaluate<true>(hand);
                            if (rank > bestRank) {
                                bestRank = rank;
                                winnersMask = m;
                            } else if (rank == bestRank) {
                                winnersMask |= m;
                            }
                        }
                        
                        // 更新全局统计
                        stats->winsByPlayerMask[winnersMask] += newWeight;
                        
                        // 使用原始手牌信息记录typeCounts和typeWins
                        if (originalPlayerHands != nullptr) {
                            for (unsigned j = 0; j < nplayers; ++j) {
                                // 使用handToType生成与getTypeEquities一致的键
            std::string key = handToType(originalPlayerHands[j].cards, true);
            stats->typeCounts[j][key] += newWeight;
            
            // 调试输出：检查K2系列手牌
            if (((originalPlayerHands[j].cards[0] / 4 == 11 && originalPlayerHands[j].cards[1] / 4 == 0) || 
                 (originalPlayerHands[j].cards[0] / 4 == 0 && originalPlayerHands[j].cards[1] / 4 == 11))) {
                std::cerr << "[ENUM-DEBUG] Recording K2 hand: " << key << " (count: " << newWeight << ")" << std::endl;
            }
            
            if (winnersMask & (1u << j)) {
                unsigned winnerCount = bitCount(winnersMask);
                double winShare = winnerCount > 0 ? (double)newWeight / winnerCount : 0.0;
                stats->typeWins[j][key] += winShare;
                
                // 调试输出：检查K2系列手牌获胜
                if (((originalPlayerHands[j].cards[0] / 4 == 11 && originalPlayerHands[j].cards[1] / 4 == 0) || 
                     (originalPlayerHands[j].cards[0] / 4 == 0 && originalPlayerHands[j].cards[1] / 4 == 11))) {
                    std::cerr << "[ENUM-DEBUG] K2 hand " << key << " won: " << winShare << std::endl;
                }
            }
                            }
                        }
                    } else {
                        // 递归调用，传递原始手牌信息
                        enumerateBoardRec(playerHands, nplayers, stats, newBoard, deck, ndeck, suitCounts,
                                      cardsLeft - repeats, i + irrelevantCount, newWeight, originalPlayerHands);
                    }
            }

            i += irrelevantCount - 1;
        } else {
            newBoard += deck[i];
            ++suitCounts[suit];
            // 递归调用，传递原始手牌信息
            enumerateBoardRec(playerHands, nplayers, stats, newBoard, deck, ndeck, suitCounts,
                              cardsLeft - 1, i + 1, weight, originalPlayerHands);
            --suitCounts[suit];
        }
    }
}

// Lookup cached results for particular preflop.
bool EquityCalculator::lookupResults(uint64_t preflopId, BatchResults& results)
{
    if (!mDeadCards && !mBoardCards && lookupPrecalculatedResults(preflopId, results))
        return true;

    std::lock_guard<std::mutex> lock(mMutex);
    auto it = mLookup.find(preflopId);
    if (it != mLookup.end())
        results = it->second;
    return it != mLookup.end();
}

// Lookup precalculated results.
bool EquityCalculator::lookupPrecalculatedResults(uint64_t preflopId, BatchResults& results) const
{
    struct Cmp
    {
        // Need two overloads because of the asymmetry.
        bool operator()(uint32_t preflopId, uint64_t a) { return preflopId < (a & 0x3fffff); }
        bool operator()(uint64_t a, uint32_t preflopId) { return (a & 0x1fffff) < preflopId; }
    };

    // Binary search.
    auto it = std::lower_bound(PRECALCULATED_2PLAYER_RESULTS.begin(), PRECALCULATED_2PLAYER_RESULTS.end(),
                               (uint32_t)preflopId, Cmp());
    if (it == PRECALCULATED_2PLAYER_RESULTS.end() || (*it & 0x3fffff) != preflopId)
        return false;

    // Unpack results.
    results.winsByPlayerMask[1] = (*it >> 22) & 0x1fffff;
    results.winsByPlayerMask[3] = (*it >> 43) & 0x1fffff;
    results.winsByPlayerMask[2] = 1712304 - results.winsByPlayerMask[1] - results.winsByPlayerMask[3];

    return true;
}

// Store results for one preflop in the lookup table.
void EquityCalculator::storeResults(uint64_t preflopId, const BatchResults& results)
{
    std::lock_guard<std::mutex> lock(mMutex); //TODO read-write lock
    mLookup.emplace(preflopId, results);
    // Make sure the hash map doesn't eat all memory. Not a great way of doing it but the lookup
    // table is quite useless with that many preflop combos anyway.
    if (mLookup.size() >= MAX_LOOKUP_SIZE)
        mLookup.clear();
}

// Transforms suits in such way that suit isomorphism can be easily detected. Goes through all the holecards, board
// cards and dead cards. First encountered suit is mapped to "virtual" suit 0, second suit maps to 1 and so on.
unsigned EquityCalculator::transformSuits(HandWithPlayerIdx* playerHands, unsigned nplayers,
                                          uint64_t* boardCards, uint64_t* deadCards)
{
    unsigned transform[SUIT_COUNT] = {~0u, ~0u, ~0u, ~0u};
    unsigned suitCount = 0;

    //TODO transform fixed cards before main enumeration loop.

    uint64_t newBoardCards = 0;
    for (unsigned i = 0; i < CARD_COUNT; ++i) {
        if ((*boardCards >> i) & 1) {
            unsigned suit = i & SUIT_MASK;
            if (transform[suit] == ~0u)
                transform[suit] = suitCount++;
            unsigned newCard = (i & RANK_MASK) | transform[suit];
            newBoardCards |= 1ull << newCard;
        }
    }
    *boardCards = newBoardCards;

    uint64_t newDeadCards = 0;
    for (unsigned i = 0; i < CARD_COUNT; ++i) {
        if ((*deadCards >> i) & 1) {
            unsigned suit = i & SUIT_MASK;
            if (transform[suit] == ~0u)
                transform[suit] = suitCount++;
            unsigned newCard = (i & RANK_MASK) | transform[suit];
            newDeadCards |= 1ull << newCard;
        }
    }
    *deadCards = newDeadCards;

    // Holecards need to be handled after any fixed cards, because the lookup is only based on them.
    for (unsigned i = 0; i < nplayers; ++i) {
        for (uint8_t& c : playerHands[i].cards) {
            unsigned suit = c & SUIT_MASK;
            if (transform[suit] == ~0u)
                transform[suit] = suitCount++;
            c = (c & RANK_MASK) | transform[suit];
        }
    }

    return suitCount;
}

// Calculates a unique 64-bit id for each combination of starting hands.
uint64_t EquityCalculator::calculateUniquePreflopId(const HandWithPlayerIdx* playerHands, unsigned nplayers)
{
    uint64_t preflopId = 0;
    // Basically we just map the preflop to a number in base 1327, where each digit represents a hand.
    for (unsigned i = 0; i < nplayers; ++i) {
        preflopId *= (CARD_COUNT * (CARD_COUNT - 1) >> 1) + 1; //1327
        auto h = playerHands[i].cards;
        if (h[0] < h[1])
            std::swap(h[0], h[1]);
        preflopId += (h[0] * (h[0] - 1) >> 1) + h[1] + 1; // map a hand to range [0, 1326]
    }
    return preflopId;
}

Hand EquityCalculator::getBoardFromBitmask(uint64_t cards)
{
    Hand board = Hand::empty();
    for (unsigned c = 0; c < CARD_COUNT; ++c) {
        if (cards & (1ull << c))
            board += c;
    }
    return board;
}

// Removes combos that conflict with board and dead cards.
std::vector<std::vector<std::array<uint8_t,2>>> EquityCalculator::removeInvalidCombos(
        const std::vector<CardRange>& handRanges, uint64_t reservedCards)
{
    std::vector<std::vector<std::array<uint8_t,2>>> result;
    for (auto& hr : handRanges) {
        result.push_back(std::vector<std::array<uint8_t,2>>{});
        for (auto& h : hr.combinations()) {
            uint64_t handMask = (1ull << h[0]) | (1ull << h[1]);
            if (!(reservedCards & handMask))
                result.back().push_back(h);
        }
    }
    return result;
}

// Work allocation for enumeration threads.
std::pair<uint64_t,uint64_t> EquityCalculator::reserveBatch(uint64_t batchCount)
{
    std::lock_guard<std::mutex> lock(mMutex);

    uint64_t totalBatchCount = getPreflopCombinationCount();
    // std::cout << "[DEBUG] totalBatchCount: " << totalBatchCount << std::endl; 
    uint64_t start = mEnumPosition;
    uint64_t end = std::min<uint64_t>(totalBatchCount, mEnumPosition + batchCount);
    mEnumPosition = end;

    return {start, end};
}

// Number of different preflops with given hand ranges, assuming no conflicts between players' hands.
uint64_t EquityCalculator::getPreflopCombinationCount()
{
    uint64_t combos = 1;
    for (unsigned i = 0; i < mCombinedRangeCount; ++i)
        combos *= mCombinedRanges[i].combos().size();
        // std::cout << "getPreflopCombinationCount: "  << combos << " " << mCombinedRangeCount << std::endl;
    return combos;
}

// Calculates size of the postflop tree, i.e. n choose k, where n is remaining deck size and k is number
// of undealt board cards.
uint64_t EquityCalculator::getPostflopCombinationCount()
{
    omp_assert(bitCount(mBoardCards) <= BOARD_CARDS);
    unsigned cardsInDeck = CARD_COUNT;
    cardsInDeck -= bitCount(mDeadCards | mBoardCards);
    cardsInDeck -= 2 * (unsigned)mHandRanges.size();
    unsigned boardCardsRemaining = BOARD_CARDS - bitCount(mBoardCards);
    uint64_t postflopCombos = 1;
    for (unsigned i = 0; i < boardCardsRemaining; ++i)
        postflopCombos *= cardsInDeck - i;
    for (unsigned i = 0; i < boardCardsRemaining; ++i)
        postflopCombos /= i + 1;
    return postflopCombos;
}

// Results aggregation for both enumeration and monte carlo.
void EquityCalculator::updateResults(const BatchResults& stats, bool threadFinished)
{
    auto t = std::chrono::high_resolution_clock::now();

    std::lock_guard<std::mutex> lock(mMutex);

    // 修改：根据是否加权选择不同的combine方法
    double batchEquity = mResults.isWeightedMonteCarlo ? 
                        combineResults_v2(stats) : 
                        combineResults(stats);

    // Store values for stdev calculation
    if (!threadFinished) {
        mBatchSum += batchEquity;
        mBatchSumSqr += batchEquity * batchEquity;
        mBatchCount += 1;
    }

    mResults.finished = threadFinished && --mUnfinishedThreads == 0;

    double dt = 1e-9 * std::chrono::duration_cast<std::chrono::nanoseconds>(t - mLastUpdate).count();
    if (mResults.time + dt >= mTimeLimit || mResults.hands + mResults.intervalHands >= mHandLimit)
        mStopped = true;

    // Periodic update through callback.
    if (dt >= mUpdateInterval || mResults.finished) {
        mResults.intervalTime = dt;
        mResults.time += mResults.intervalTime;
        mResults.hands += mResults.intervalHands;
        mResults.intervalSpeed = mResults.intervalHands / (mResults.intervalTime + 1e-9);
        mResults.speed = mResults.hands / (mResults.time + 1e-9);
        mResults.intervalHands = 0;
        mResults.stdev = std::sqrt(1e-9 + mBatchSumSqr - mBatchSum * mBatchSum / mBatchCount) / mBatchCount;
        mResults.stdevPerHand = mResults.stdev * std::sqrt(mResults.hands);
        if (mResults.enumerateAll) {
            mResults.progress = (double)mEnumPosition / getPreflopCombinationCount();
        } else {
            double estimatedHands = std::pow(mResults.stdev / mStdevTarget, 2) * mResults.hands;
            mResults.progress = mResults.hands / estimatedHands;
        }
        mResults.preflopCombos = getPreflopCombinationCount();

        if (!mResults.enumerateAll && mResults.stdev < mStdevTarget)
            mStopped = true;
        // 调试输出
        // if (mResults.finished) {
        //     std::cout << "\nFinal Results:\n";
        //     for (unsigned i = 0; i < mResults.players; ++i) {
        //         std::cout << "Player " << i << " Equity: " << std::fixed << std::setprecision(4) 
        //                 << mResults.equity[i] * 100 << "%\n";
        //     }
        //     std::cout << "Total weight sum: " << mResults.totalAllSum << std::endl;
        // }
        
        double totalWinSum = 0;
        for (unsigned i = 0; i < mResults.players; ++i) {
            totalWinSum += mResults.wins[i];
        }
        for (unsigned i = 0; i < mResults.players; ++i) {

            if (mResults.isWeightedMonteCarlo) {
            // 加权模式：总权重归一化（唯一计算点）
            mResults.equity[i] = mResults.totalAllSum > 1e-20 
                ? mResults.wins[i] / mResults.totalAllSum 
                : 0.0;
            } else {
                mResults.equity[i] = (mResults.wins[i] + mResults.ties[i]) / (mResults.hands + 1e-9);
                }
        }

        mUpdateResults = mResults;

        if (mCallback)
            mCallback(mResults);

        mLastUpdate = t;
    }
}

// Sum batch results in the main results structure.
double EquityCalculator::combineResults(const BatchResults& batch)
{
    uint64_t batchHands = 0;
    double batchEquity = 0;

    for (unsigned i = 0; i < (1u << mResults.players); ++i) {
        mResults.intervalHands += batch.winsByPlayerMask[i];
        batchHands += batch.winsByPlayerMask[i];
        unsigned winnerCount = bitCount(i);
        unsigned actualPlayerMask = 0;
        for (unsigned j = 0; j < mResults.players; ++j) {
            if (i & (1 << j)) {
                if (winnerCount == 1) {
                    mResults.wins[batch.playerIds[j]] += batch.winsByPlayerMask[i];
                    if (batch.playerIds[j] == 0)
                        batchEquity += batch.winsByPlayerMask[i];
                } else {
                    mResults.ties[batch.playerIds[j]] += batch.winsByPlayerMask[i] / (double)winnerCount;
                    if (batch.playerIds[j] == 0)
                        batchEquity += batch.winsByPlayerMask[i] / (double)winnerCount;
                }
                actualPlayerMask |= 1 << batch.playerIds[j];
            }
        }
        mResults.winsByPlayerMask[actualPlayerMask] += batch.winsByPlayerMask[i];
    }

    mResults.evaluations += batch.evalCount;
    mResults.skippedPreflopCombos += batch.skippedPreflopCombos;
    mResults.evaluatedPreflopCombos += batch.uniquePreflopCombos;

    return batchEquity / (batchHands + 1e-9);
}

// Helper function for printing out precalculated lookup tables.
void EquityCalculator::outputLookupTable() const
{
    std::vector<std::array<unsigned,3>> a;
    for (auto& e: mLookup) {
        a.push_back({(unsigned)e.first, (unsigned)e.second.winsByPlayerMask[1],
                     (unsigned)e.second.winsByPlayerMask[3]});
    }
    std::sort(a.begin(), a.end(), [](const std::array<unsigned,3>& lhs, const std::array<unsigned,3>& rhs){
        return lhs[0] < rhs[0];
    });

    std::cout << std::hex;
    for (size_t i = 0; i < a.size(); ++i) {
        if (i % 6 == 0)
            std::cout << std::endl;
        std::cout << " 0x" << ((uint64_t)a[i][0] | (uint64_t)a[i][1] << 22 | (uint64_t)a[i][2] << 43) << ",";
    }
    std::cout << std::endl;
    std::cout.flush();
}

//Not used atm.
const std::vector<uint64_t> EquityCalculator::PRECALCULATED_2PLAYER_RESULTS {
};

}